/****************************************************
 * 创建人：@author fengxin    
 * 创建时间: 2019/12/21/16:12
 * 项目名称: SpringCodeGenFX
 * 文件名称: SpringCodeGenServiceImpl.java
 * 文件描述: SpringCodeGenService实现类
 *
 * All rights Reserved, Designed By 投资交易团队
 * @Copyright:2016-2019
 *
 ********************************************************/
package com.ysstech.codegen.service.impl;

import com.sun.javafx.binding.StringFormatter;
import com.ysstech.codegen.component.FreemarkerComponent;
import com.ysstech.codegen.constant.GlobalConst;
import com.ysstech.codegen.entity.ClassInfo;
import com.ysstech.codegen.entity.ConfigInfo;
import com.ysstech.codegen.entity.LabelInfo;
import com.ysstech.codegen.entity.ParamInfo;
import com.ysstech.codegen.exception.CodeGenerateException;
import com.ysstech.codegen.service.SpringCodeGenService;
import com.ysstech.codegen.util.*;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;


import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 包名称：com.ysstech.codegen.service.impl
 * 类名称：SpringCodeGenServiceImpl
 * 类描述：SpringCodeGenService实现类
 * 创建人：@author fengxin
 * 创建时间：2019/12/21/16:12
 */


@Slf4j
public class SpringCodeGenServiceImpl implements SpringCodeGenService {

    FreemarkerComponent freemarkerComponent;

    String appWorkDirPath;
    String userConfigPath;
    String defaultConfigPath;


    ParamInfo paramInfo;

    Set<String> selectedCodeLabels = new HashSet<>();

    Map<String, LabelInfo> labelInfoMap;
    Map<String, String> labelToSubPackageMap;
    Map<String, String> labelToFileExtMap;
    Map<String, String> labelToTemplateNameMap;

    @Override
    public String getAppWorkDirPath() {
        return appWorkDirPath;
    }

    @Override
    public Set<String> getSelectedCodeLabels() {
        return selectedCodeLabels;
    }

    @Override
    public void addSelectedCodeLabel(String label) {
        selectedCodeLabels.add(label);
    }

    @Override
    public void removeSelectedCodeLabel(String label) {
        selectedCodeLabels.remove(label);
    }


    public SpringCodeGenServiceImpl() {

        // 获取文件执行目录 设置为默认工作目录
        File appWorkDir = new File("");
        appWorkDirPath = appWorkDir.getAbsolutePath().replace("\\", "/");
        log.info(String.format("appWorkDirPath is set to [%s]", appWorkDirPath));

    }

    public SpringCodeGenServiceImpl(String appWorkDirPath) {
        this.initWorkDirPath(appWorkDirPath);
    }

    @Override
    public void initWorkDirPath(String appWorkDirPath) {

        this.appWorkDirPath = appWorkDirPath;
        // 初始化用户自定义配置文件路径为工作目录下的sqlgen/config
        this.userConfigPath = this.appWorkDirPath + String.format("/%s", GlobalConst.CONFIG_FILE_PATH);

    }


    @Override
    public void loadConfig() {
        try {
            // 获取配置信息
            log.info("开始加载用户配置文件【" + userConfigPath + "】");
            File file = new File(userConfigPath);
            if (!file.exists()) {
                log.info("用户配置文件【" + userConfigPath + "】不存在,开始加载默认配置文件");
                loadDefaultConfig();
                return;
            }
            InputStream inputStream = new FileInputStream(userConfigPath);
            ConfigInfo configInfo = SpringCodeGenUtil.getConfigInfo(inputStream);
            inputStream.close();
            parseConfig(configInfo);
            // 初始化 freemarker组件
            String templateDir = appWorkDirPath + "/" + GlobalConst.TEMPLATE_DIR;
            log.info("模板文件路径设置为【" + templateDir + "】");
            freemarkerComponent = new FreemarkerComponent(templateDir);
        } catch (Throwable e) {
            log.error("加载配置文件【" + userConfigPath + "】错误", new CodeGenerateException(e));
        }
    }

    public void loadDefaultConfig() {
        try {

            URL urlJar = this.getClass().getClassLoader().getResource(GlobalConst.CONFIG_FILE_PATH);
            URL urlInstallDir = this.getClass().getClassLoader().getResource(GlobalConst.CONFIG_FILE_NAME);
            URL url = urlInstallDir;
            String templateClasspathDir = GlobalConst.TEMPLATE_RELATIVE_DIR;

            if (Objects.isNull(urlInstallDir)) {
                log.info(String.format("开始获取jar包默认配置文件【%s】", defaultConfigPath));
                url = urlJar;
                templateClasspathDir = GlobalConst.TEMPLATE_DIR;
            } else {
                log.info(String.format("开始获取安装目录默认配置文件【%s】", defaultConfigPath));
            }

            defaultConfigPath = url.getPath();
            URLConnection conn = url.openConnection();
            InputStream inputStream = conn.getInputStream();
            ConfigInfo configInfo = SpringCodeGenUtil.getConfigInfo(inputStream);
            inputStream.close();
            parseConfig(configInfo);
            freemarkerComponent = new FreemarkerComponent(this.getClass().getClassLoader(),templateClasspathDir);

        } catch (Exception e) {
            throw new CodeGenerateException("获取默认配置文件失败", e);
        }
    }

    @Override
    public void genCode(String fileText) {
        try {

            // 创建文件夹
            String sqlGenDirPath = appWorkDirPath + String.format("/%s", GlobalConst.PLUGIN_WORK_DIR);
            String genCodePath = appWorkDirPath + String.format("/%s", GlobalConst.CODE_FILE_PATH);
            FileUtils.makeDir(sqlGenDirPath);
            FileUtils.makeDir(genCodePath);

            // Spring Boot Application的基础包名
            String basePackageName = paramInfo.getPackageName();

            // 开始解析sql脚本生成代码
            // sql脚本中的sql语句拆分，每个sql语句分别处理
            // 替换注释中的英文分号;(否则按分号拆分时会出错)
            fileText = StringUtils.lowerCase(fileText);
            fileText = fileText.replaceAll("(comment\\s+'[^=]*?);([\\s\\S]*?',)", "$1，$2");
            String[] sqlStrs = fileText.split(";");
            for (String sql : sqlStrs) {
                if (SqlUtils.isNotCreateTable(sql)) {
                    // 不是create table语句，直接跳过
                    continue;
                }
                log.debug(String.format("sql:\n%s", sql));
                // 设置sql语句
                paramInfo.setTableSql(sql);
                for (String label : selectedCodeLabels) {
                        String resultCode = this.genCodeForOneLabel(paramInfo, label);
                        if (!StringUtils.isEmpty(resultCode)) {
                            SpringCodeGenUtil.saveCodeText(genCodePath, label, resultCode, basePackageName + "." + labelToSubPackageMap.get(label), labelToFileExtMap.get(label));
                        }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new CodeGenerateException("生成代码失败", e);
        }
    }

    private String genCodeForOneLabel(ParamInfo paramInfo, String label) throws Exception {

        log.info("paramInfo:{}，label:{}", paramInfo.toString(),label);

        if (StringUtils.isBlank(paramInfo.getTableSql())) {
            throw new CodeGenerateException("表结构信息不可为空");
        }

        // parse table
        ClassInfo classInfo = null;
        switch (paramInfo.getDataType()) {
            //parse json
            case "json":
                classInfo = TableParseUtils.processJsonToClassInfo(paramInfo);
                break;
            //parse sql  by regex
            case "sql-regex":
                classInfo = TableParseUtils.processTableToClassInfoByRegex(paramInfo);
                break;
            case "SQL":
                classInfo = TableParseUtils.processTableIntoClassInfo(paramInfo);
                break;
            default:
                break;
        }

        // process the param
        Map<String, Object> params = new HashMap<String, Object>(8);

        // 处理ClassInfo 将基类字段从classFiledList中筛除
        classInfo = SpringCodeGenUtil.processClassInfo(classInfo, paramInfo);

        // 设置类型信息
        params.put("classInfo", classInfo);
        // 设置作者名称
        params.put("authorName", paramInfo.getAuthorName());
        // 当前文件包名调整为基础包加上类名小写化
//            params.put("packageName", paramInfo.getPackageName()+"."+StringUtils.lowerCase(classInfo.getClassName()));
        params.put("packageName", paramInfo.getPackageName());
        // 设置目标子包名
        params.put("subPackageName", labelToSubPackageMap.get(label));
        // 设置congtoller返回类型
        params.put("returnUtil", paramInfo.getReturnUtil());
        // 设置idea的Module名称
        params.put("moduleName", paramInfo.getModuleName());

        log.info("generator table:" + (classInfo == null ? "" : classInfo.getTableName())
                + ",field size:" + ((classInfo == null || classInfo.getFieldList() == null) ? "" : classInfo.getFieldList().size()));

        // generate the code 需要加新的模板请自定义并修改配置文件
        String result;
        try {
            result = getResultByParamsAndLabel(params, label);
        } catch (Exception e) {
            log.error(String.format("paramInfo:%s，label:%s", paramInfo.toString(),label),e);
            return "";
        }
        return result;
    }

    @Override
    public String getResultByParamsAndLabel(Map<String, Object> params, String label) throws IOException, TemplateException {
        String templateName = labelToTemplateNameMap.get(label);
        if (templateName == null) {
            throw new CodeGenerateException("label[" + label + "]的templateName没有配置,请在" + userConfigPath + "中添加");
        }
        if (freemarkerComponent == null) {
            throw new CodeGenerateException("freemarkerComponent is null");
        }
        templateName = paramInfo.getModelTypeEnum().getName() + templateName;
        String result = freemarkerComponent.processString(templateName, params);
        return result;
    }


    public void parseConfig(ConfigInfo configInfo) {
        paramInfo = configInfo.getParamInfo();
        // 缺失值处理
        if (paramInfo == null) {
            paramInfo = new ParamInfo();
        }
        String basePackageName = paramInfo.getPackageName();

        if (StringUtils.isEmpty(basePackageName)) {
            basePackageName = GlobalConst.PLUGIN_WORK_DIR + "." + GlobalConst.CODE_RELATIVE_DIR;
        }

        String responseType = paramInfo.getReturnUtil();
        responseType = StringUtils.isEmpty(responseType) ? "ServerResponse" : responseType;
        String tinyintTransType = paramInfo.getTinyintTransType();
        tinyintTransType = StringUtils.isEmpty(tinyintTransType) ? "boolean" : tinyintTransType;
        String authorName = paramInfo.getAuthorName();
        authorName = StringUtils.isEmpty(authorName) ? System.getProperty("user.name") : authorName;
        String nameCaseType = paramInfo.getNameCaseType();
        nameCaseType = StringUtils.isEmpty(nameCaseType) ? "CamelCase" : nameCaseType;
        paramInfo.setAuthorName(authorName);
        paramInfo.setNameCaseType(nameCaseType);
        paramInfo.setPackageName(basePackageName);
        paramInfo.setReturnUtil(responseType);
        paramInfo.setTinyintTransType(tinyintTransType);

        // 获取忽略字段列表
        List<String> baseEntityFields = paramInfo.getBaseEntityFields();

        // 缺失值处理
        if (baseEntityFields == null) {
            baseEntityFields = new ArrayList<>();
            paramInfo.setBaseEntityFields(baseEntityFields);
        }

        // 审核字段列表
        List<String> auditFields = paramInfo.getAuditFields();

        // 缺失值处理
        if (auditFields == null) {
            auditFields = new ArrayList<>();
            paramInfo.setAuditFields(auditFields);
        }

        // 获取忽略字段列表
        List<String> baseReqFields = paramInfo.getBaseReqFields();

        // 缺失值处理
        if (baseReqFields == null) {
            baseReqFields = new ArrayList<>();
            paramInfo.setBaseReqFields(baseReqFields);
        }

        // 获取忽略字段列表
        List<String> baseRepFields = paramInfo.getBaseRepFields();

        // 缺失值处理
        if (baseRepFields == null) {
            baseRepFields = new ArrayList<>();
            paramInfo.setBaseRepFields(baseRepFields);
        }
        // 获取labelInfoMap
        labelInfoMap = configInfo.getLabelInfoMap();
        // 缺失值处理
        if (labelInfoMap == null || labelInfoMap.size() == 0) {
            throw new CodeGenerateException("配置信息【labelInfoMap】缺失，无法生成代码");
        }

        labelToTemplateNameMap = labelInfoMap.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().getTemplateName()));
        labelToFileExtMap = labelInfoMap.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().getTargetFileExt()));
        labelToSubPackageMap = labelInfoMap.entrySet().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue().getTargetSubPackage()));
log.info("msg");
    }


    @Override
    public ParamInfo getParamInfo() {
        return paramInfo;
    }

    @Override
    public Map<String, LabelInfo> getLabelInfoMap() {
        return labelInfoMap;
    }

    @Override
    public Map<String, String> getLabelToSubPackageMap() {
        return labelToSubPackageMap;
    }

    @Override
    public Map<String, String> getLabelToTemplateNameMap() {
        return labelToTemplateNameMap;
    }

    @Override
    public Map<String, String> getLabelToFileExtMap() {
        return labelToFileExtMap;
    }


}
